/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is dual-licensed under either the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree or the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree. You may select, at your option, one of the
 * above-listed licenses.
 */

package main

import (
	"bytes"
	"strings"
	"testing"
	"text/template"
)

func TestTargetTemplate(t *testing.T) {
	tests := []struct {
		name           string
		config         BuckConfig
		target         *BuckTarget
		expectedOutput string
		notExpected    []string
	}{
		{
			name: "binary target with common deps and no target label prefix",
			config: BuckConfig{
				Preambule:         "# Generated by gobuckify\n",
				LoadGoBinaryRule:  "load(\":defs.bzl\", \"go_binary\")\n",
				GoBinaryRule:      "go_binary",
				DepsAttr:          "go_external_deps",
				GenerateEmbedSrcs: true,
			},
			target: &BuckTarget{
				Name:       "test_binary",
				ImportPath: "github.com/example/test_binary",
				IsBinary:   true,
				EmbedFiles: *newFromList([]string{"embed1.txt", "embed2.txt"}),
				CommonDeps: []string{"github.com/example/dep1", "github.com/example/dep2"},
			},
			expectedOutput: `
# Generated by gobuckify
load(":defs.bzl", "go_binary")
go_binary(
    name = "test_binary",
    package_name = "github.com/example/test_binary",
    srcs = native.glob(["*.go", "*.s", "*.h", "*.c", "*.cc", "*.cpp", "*.S"]),
    header_namespace = "",
    go_external_deps = [
        "github.com/example/dep1",
        "github.com/example/dep2",
    ],
    embed_srcs = [
        "embed1.txt",
        "embed2.txt",
    ],
    visibility = ["PUBLIC"],
)`,
			notExpected: []string{
				"go_library",
				"target_compatible_with",
			},
		},
		{
			name: "library target with platform deps",
			config: BuckConfig{
				Preambule:             "# Generated by gobuckify\n",
				LoadGoLibraryRule:     "load(\":defs.bzl\", \"go_library\")\n",
				GoLibraryRule:         "go_library",
				DepsAttr:              "deps",
				DepsTargetLabelPrefix: "//third-party/go/",
				GenerateEmbedSrcs:     false,
			},
			target: &BuckTarget{
				Name:       "test_lib",
				ImportPath: "github.com/example/test_lib",
				IsBinary:   false,
				CommonDeps: []string{"github.com/example/common_dep"},
				PlatformDeps: map[string]*OSDeps{
					"linux": {
						OS: "linux",
						ArchDeps: map[string]*ArchDeps{
							"x86_64": {
								Arch: "x86_64",
								Deps: func() *stringSet {
									s := newSet()
									s.Add("github.com/example/linux_dep")
									return s
								}(),
							},
						},
					},
				},
			},
			expectedOutput: `
# Generated by gobuckify
load(":defs.bzl", "go_library")
go_library(
    name = "test_lib",
    package_name = "github.com/example/test_lib",
    srcs = native.glob(["*.go", "*.s", "*.h", "*.c", "*.cc", "*.cpp", "*.S"]),
    header_namespace = "",
    deps = [
        "//third-party/go/github.com/example/common_dep:common_dep",
    ] + select({
        "DEFAULT": [],
        "linux": select({
            "DEFAULT": [],
            "x86_64": [
                "//third-party/go/github.com/example/linux_dep:linux_dep",
            ],
        }),
    }),
    visibility = ["PUBLIC"],
)`,
			notExpected: []string{
				"go_binary",
				"embed_srcs",
			},
		},
		{
			name: "library target with no deps",
			config: BuckConfig{
				Preambule:         "# Generated by gobuckify\n",
				LoadGoLibraryRule: "load(\":defs.bzl\", \"go_library\")\n",
				GoLibraryRule:     "go_library",
				DepsAttr:          "deps",
			},
			target: &BuckTarget{
				Name:       "simple_lib",
				ImportPath: "github.com/example/simple_lib",
				IsBinary:   false,
			},
			expectedOutput: `
# Generated by gobuckify
load(":defs.bzl", "go_library")
go_library(
    name = "simple_lib",
    package_name = "github.com/example/simple_lib",
    srcs = native.glob(["*.go", "*.s", "*.h", "*.c", "*.cc", "*.cpp", "*.S"]),
    header_namespace = "",
    visibility = ["PUBLIC"],
)`,
			notExpected: []string{
				"deps =",
				"embed_srcs",
				"target_compatible_with",
			},
		},
		{
			name: "library target with platform deps only",
			config: BuckConfig{
				Preambule:             "# Generated by gobuckify\n",
				LoadGoLibraryRule:     "load(\":defs.bzl\", \"go_library\")\n",
				GoLibraryRule:         "go_library",
				DepsAttr:              "deps",
				DepsTargetLabelPrefix: "//third-party/go/",
			},
			target: &BuckTarget{
				Name:       "platform_lib",
				ImportPath: "github.com/example/platform_lib",
				IsBinary:   false,
				CommonDeps: []string{}, // No common deps
				PlatformDeps: map[string]*OSDeps{
					"linux": {
						OS: "linux",
						ArchDeps: map[string]*ArchDeps{
							"x86_64": {
								Arch: "x86_64",
								Deps: func() *stringSet {
									s := newSet()
									s.Add("github.com/example/linux_x86_dep")
									return s
								}(),
							},
							"arm64": {
								Arch: "arm64",
								Deps: func() *stringSet {
									s := newSet()
									s.Add("github.com/example/linux_arm_dep")
									return s
								}(),
							},
						},
					},
					"darwin": {
						OS: "darwin",
						ArchDeps: map[string]*ArchDeps{
							"x86_64": {
								Arch: "x86_64",
								Deps: func() *stringSet {
									s := newSet()
									s.Add("github.com/example/darwin_dep")
									return s
								}(),
							},
						},
					},
				},
				TargetCompatibleWith: map[string][]string{
					"linux":  {"x86_64", "arm64"},
					"darwin": {"x86_64"},
				},
			},
			expectedOutput: `
# Generated by gobuckify
load(":defs.bzl", "go_library")
go_library(
    name = "platform_lib",
    package_name = "github.com/example/platform_lib",
    srcs = native.glob(["*.go", "*.s", "*.h", "*.c", "*.cc", "*.cpp", "*.S"]),
    header_namespace = "",
    deps = [
    ] + select({
        "DEFAULT": [],
        "darwin": select({
            "DEFAULT": [],
            "x86_64": [
                "//third-party/go/github.com/example/darwin_dep:darwin_dep",
            ],
        }),
        "linux": select({
            "DEFAULT": [],
            "arm64": [
                "//third-party/go/github.com/example/linux_arm_dep:linux_arm_dep",
            ],
            "x86_64": [
                "//third-party/go/github.com/example/linux_x86_dep:linux_x86_dep",
            ],
        }),
    }),
    target_compatible_with = select({
        "DEFAULT": ["config//:none"],
        "darwin": select({
            "DEFAULT": ["config//:none"],
            "x86_64": [],
        }),
        "linux": select({
            "DEFAULT": ["config//:none"],
            "x86_64": [],
            "arm64": [],
        }),
    }),
    visibility = ["PUBLIC"],
)`,
			notExpected: []string{
				"go_binary",
				"embed_srcs",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tmpl := template.New("test")
			tmpl.Funcs(template.FuncMap{
				"targetLabelFromImportPath": targetLabelFromImportPath,
			})
			tmpl, err := tmpl.Parse(targetTempate)
			if err != nil {
				t.Fatalf("Failed to parse template: %v", err)
			}

			var buf bytes.Buffer
			err = tmpl.Execute(&buf, templateData{
				Config: tt.config,
				Target: tt.target,
			})
			if err != nil {
				t.Fatalf("Template execution failed: %v", err)
			}

			result := buf.String()

			// Check for expected output
			if !strings.Contains(result, strings.TrimSpace(tt.expectedOutput)) {
				t.Errorf("Expected output to contain %q, but it didn't.\nOutput:\n%s", tt.expectedOutput, result)
			}

			// Check for not expected output
			for _, notExpected := range tt.notExpected {
				if strings.Contains(result, notExpected) {
					t.Errorf("Expected output to NOT contain %q, but it did.\nOutput:\n%s", notExpected, result)
				}
			}
		})
	}
}
